//this file is part of ePopup
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "popup.h"
#include "popupDlg.h"
#include "PopupClient.h"
#include "PopupServer.h"
#include "OtherFunctions.h"
#include "PopupLibUtilities.h"
#include "ResourceEntry.h"
#include "PopupClientThread.h"
#include "PopupServerThread.h"
#include <vector>
#include <sstream>
#include <string>

using namespace std;

CMutex CpopupApp::s_clientmutex;

///////////////////////////////////////////////////////////////////////////////
// CpopupApp


CpopupApp::CpopupApp(LPCTSTR lpszAppName)
	:CWinApp(lpszAppName), popupdlg(0), m_popupclient(0), m_popupserver(0), 
	m_resetTimer(true), m_automode(POPUP_MODE_MUTED), m_automodeDelay(0)
{
}

CpopupApp theApp(_T("ePopup"));


BOOL CpopupApp::InitInstance()
{
	CWinApp::InitInstance();
	
	if (!loadEnvironment())
	{
		AfxMessageBox(_T("Failed everything!! I'm a crap!"));
		::ExitProcess(1);
	}

	// Create/Touch user directory
	if (!initUserDirectory())
	{
		AfxMessageBox(_T("Failed to create user dir"));
		::ExitProcess(1);
	}

	// Load options
	if (!loadOptions())
	{
		AfxMessageBox(_T("Failed to load options"));
		::ExitProcess(1);
	}

	// Load resources
	if (!loadResources())
	{
		AfxMessageBox(_T("Warning : no resource loaded!"));
	}

	// Init logger
	m_logger.setPath(MFCStringToSTLString(m_userdir));

	// Create main dialog
	CpopupDlg *dlg = new CpopupDlg();
	popupdlg = dlg;
	m_pMainWnd = dlg;

	// Main window (which owns the main thread!)
	dlg->DoModal();
	delete dlg;
	popupdlg = NULL;

	return FALSE;
}

void CpopupApp::exitApp()
{
	popupdlg->EndDialog(0);
}

bool CpopupApp::loadEnvironment()
{
	ULONG size = USER_NAME_MAX_LENGTH;
	TCHAR user[USER_NAME_MAX_LENGTH];
	char hostname[HOSTNAME_MAX_LENGTH];

	bool _res = true;

	// Get user name
	if (GetUserName(user, &size) == 0)
	{
		_res = false;
	}
	else 
	{
		user[size] = '\0';
		m_username = user;
	}

	// Start WSA (Fucking MFC!)
	WORD wVersionRequested;
	wVersionRequested = MAKEWORD( 2, 2 );
	WSADATA wsaData;
	int _errcode = WSAStartup( wVersionRequested, &wsaData );
	
	// Get hostname
	if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR)
	{
		_errcode = WSAGetLastError();
		AfxMessageBox(_T("Failed to get hostname! Exiting"));
		_res = false;
	}
	else
	{
		m_hostname = hostname;
	}

	// Set user directory and ini file
	TCHAR tcDirName[_MAX_PATH];
	GetEnvironmentVariable(_T("userprofile"), tcDirName, _MAX_PATH);
	m_userdir = CString(tcDirName) + _T("\\popup");
	m_tempdir = m_userdir + _T("\\temp");
	m_inifile = m_userdir + _T("\\popup.ini");

	return _res;
}

bool CpopupApp::initUserDirectory()
{
	bool _rc = false;

	if (CreateDirectory(m_userdir, NULL) != 0 ||
		GetLastError() == ERROR_ALREADY_EXISTS)
	{
		deleteDirectory(m_tempdir);

		if (CreateDirectory(m_tempdir, NULL) != 0 ||
			GetLastError() == ERROR_ALREADY_EXISTS)
		{
			_rc = true;
		}
	}
	return _rc;
}

ResourceEntry *CpopupApp::getRandomResource()
{
	ResourceEntry *_res = NULL;

	if (m_resources.size() > 0)
	{
		int _randomIndex = rand() % m_resources.size();
		PopupResources::iterator it = m_resources.begin();
		for (int i = 0; i < _randomIndex; it++, i++);
		_res = it->second;
	}
	return _res;
}

bool CpopupApp::saveOptions()
{
	string opt, app, args;
	int n;

	if (!m_options.getOption(POPUP_RES_PATH, opt)) {
		m_options.setOption(POPUP_RES_PATH, MFCStringToSTLString(m_userdir));
	}
	if (!m_options.getOption(SERVER_HOSTNAME, opt)) {
		m_options.setOption(SERVER_HOSTNAME, "127.0.0.1");
	}
	if (!m_options.getIntOption(SERVER_PORT, n)) {
		m_options.setIntOption(SERVER_PORT, 2000);
	}
	if (!m_options.getOption(CLIENT_USER_NAME, opt)) {
		m_options.setOption(CLIENT_USER_NAME, MFCStringToSTLString(m_username));
	}
	if (!m_options.getOption(AUTO_MODE, opt)) {
		m_options.setOption(AUTO_MODE, "normal");
	}
	if (!m_options.getIntOption(AUTO_MODE_DELAY, n)) {
		m_options.setIntOption(AUTO_MODE_DELAY, 120);
	}
	if (!m_options.getOption(WEB_BROWSER_PATH, opt)) {
		m_options.setOption(WEB_BROWSER_PATH, "iexplore.exe");
	}
	if (!getApplicationByExtension(APP_DEFAULT_EXTENSION, app, args)) {
		m_options.setOption(APP_DEFAULT_EXTENSION, "explorer.exe", APP_ASSOCIATIONS_KEY);
	}
	return m_options.save(MFCStringToSTLString(m_inifile));
}

bool CpopupApp::loadOptions()
{
	bool _res = m_options.load(MFCStringToSTLString(m_inifile));
	
	// Save options to force mandatory options to be set!
	if (_res != true)
	{
		AfxMessageBox(_T("Failed to load options. Applying defauls!"));
	}
	
	_res = saveOptions();	

	// Get server ip and port from option file
	string _server;
	int _port = 0;
	
	_res = (m_options.getOption(SERVER_HOSTNAME, _server) &&
			m_options.getIntOption(SERVER_PORT, _port));

	if (_res == true)
	{
		m_servername = _server;
		m_serverport = _port;
	}
		
	// Eventually, load user name, and auto mode options (not mandatory)
	string _username;
	string _automode;
	int _automodeDelay;

	if (m_options.getOption(CLIENT_USER_NAME, _username))
	{
		m_username = CString(_username.c_str());
	}
	if (m_options.getOption(AUTO_MODE, _automode))
	{
		m_automode = stringToPopupMode(_automode);
	}
	if (m_options.getIntOption(AUTO_MODE_DELAY, _automodeDelay))
	{
		m_automodeDelay = _automodeDelay;
	}

	return _res;
}

CString g_rcPath("");

bool CpopupApp::loadResources()
{
	CFileFind finder;
	string tmp;
	string resourcePath;

	// Get resource path
	m_options.getOption(POPUP_RES_PATH, resourcePath);
	absolutePath(resourcePath, resourcePath);
	m_options.setOption(POPUP_RES_PATH, resourcePath);
	m_logger << "Resource path = " << resourcePath << endl;
	m_logger.flush();

	// Open it!
	g_rcPath = (resourcePath + "\\*").c_str();
	BOOL ok = finder.FindFile(g_rcPath);
	
	if (ok) m_resources.clear();

	while (ok)
	{
		ok = finder.FindNextFile();

		if (finder.IsDirectory() && !finder.IsDots())
		{
			m_logger << _T("Loading resources from ... ") << finder.GetFileName() << endl;
			loadResourceDir(finder.GetFilePath());
		}
	}

	m_logger.flush();
	
	dumpResources();

	return (m_resources.size() > 0);
}

bool CpopupApp::loadResourceDir(const CString & p_dir)
{
	CFileFind finder;
	ResourceEntry *_resource = new ResourceEntry();

	BOOL ok = finder.FindFile(p_dir + _T("\\*"));
	
	while (ok)
	{
		ok = finder.FindNextFile();

		if (!finder.IsDirectory() && !finder.IsDots())
		{
			CString filePath = finder.GetFilePath();
			if (isImage(filePath)) {
				_resource->m_imagesPath.push_back(filePath);
			} else if (isSound(filePath)) {
				_resource->m_soundsPath.push_back(filePath);
			} else if (isVideo(filePath)) {
				_resource->m_videosPath.push_back(filePath);
			}
		}
	}

	if (_resource->m_imagesPath.size() > 0 ||
		_resource->m_soundsPath.size() > 0 ||
		_resource->m_videosPath.size() > 0 ) {
		m_resources[basename(p_dir)] = _resource;
		return true;
	} else {
		delete _resource;
		return false;
	}
}

bool CpopupApp::dumpResources()
{
	CpopupApp::PopupResources::iterator _it = m_resources.begin();

	m_logger << "  List of resources" << endl;
	m_logger << "  ................." << endl;
	m_logger.flush();
	
	
	while (_it != m_resources.end())
	{
		ResourceEntry *_rc = _it->second;

		// Dump images
		m_logger << "    [images]" << endl;
		vector<CString>::iterator _itImage = _rc->m_imagesPath.begin();
		while (_itImage != _rc->m_imagesPath.end())
		{
			m_logger << _T("      + ") << (*_itImage) << endl;
			_itImage++;
		}
		// Dump sounds
		m_logger << "    [sounds]" << endl;
		vector<CString>::iterator _itSound = _rc->m_soundsPath.begin();
		while (_itSound != _rc->m_soundsPath.end())
		{
			m_logger << _T("      + ") << (*_itSound) << endl;
			_itSound++;
		}
		// Dump sounds
		m_logger << "    [videos]" << endl;
		vector<CString>::iterator _itvideo = _rc->m_videosPath.begin();
		while (_itvideo != _rc->m_videosPath.end())
		{
			m_logger << _T("      + ") << (*_itvideo) << endl;
			_itvideo++;
		}
		m_logger.flush();
		_it++;
	}
	return true;
}

bool CpopupApp::restartNetwork()
{
	if (m_popupserver != 0)
	{
		m_popupserver->kill();
	}
	else
	{
		popupdlg->startServer();
	}

	if (m_popupclient != 0) 
	{
		m_popupclient->kill();
	}
	
	return true;
}

bool CpopupApp::getApplicationByExtension(const string & p_extension, string & p_app, string & p_args, bool _setDefault)
{
	bool _rc = true;
	string _extension = p_extension;

	// Try to find extension application first
	if (m_options.getOption(_extension, p_app, APP_ASSOCIATIONS_KEY) == false)
	{
		// If not found, try to get default one
		if (_setDefault && m_options.getOption(APP_DEFAULT_EXTENSION, p_app, APP_ASSOCIATIONS_KEY))
		{
			_extension = APP_DEFAULT_EXTENSION;
		}
		// If still nothing, we give up!
		else
		{
			_rc = false;
		}
	}

	// Find arguments associated to the application
	if (_rc == true)
	{
		stringstream _argskey;
		_argskey << p_extension << APP_ARGS_SUFFIX;
		m_options.getOption(_argskey.str(), p_args, APP_ASSOCIATIONS_KEY);
	}
	
	return _rc;
}

bool CpopupApp::openFile(const string & p_file)
{
	// First of all, we have to retrieve the appropriate application
	// to open the selected file
	//---------------------
	
	// Let extract the file extension
	string _extension;
	if (getFileExtension(p_file, _extension) != true)
	{
		_extension = APP_DEFAULT_EXTENSION;
	}

	// Now let's try to find an appropriate application...
	//----------------------
	
	string _app, _args;
	if (getApplicationByExtension(_extension, _app, _args))
	{
		// Build application arguments
		stringstream _s;
		_s << "\"" << p_file << "\" " << _args;
		CString _applicationArgs(_s.str().c_str());
		CString _applicationToUse(_app.c_str());

		SHELLEXECUTEINFO ExecuteInfo;    
		memset(&ExecuteInfo, 0, sizeof(ExecuteInfo));
    
		ExecuteInfo.cbSize       = sizeof(ExecuteInfo);
		ExecuteInfo.fMask        = 0;                
		ExecuteInfo.hwnd         = 0;                
		ExecuteInfo.lpVerb       = _T("open");                      
		ExecuteInfo.lpFile       = _applicationToUse;
		ExecuteInfo.lpParameters = _applicationArgs;
		ExecuteInfo.lpDirectory  = 0;
		ExecuteInfo.nShow        = SW_SHOW;
		ExecuteInfo.hInstApp     = 0;    
		if (ShellExecuteEx(&ExecuteInfo) == FALSE)
		{
			AfxMessageBox(_T("Failed to open file"));
		}
		else
		{
			return true;
		}
	}
	else
	{
		AfxMessageBox(_T("Failed to find default application => no way to open file!"));
	}

	return false;
}

bool CpopupApp::openURL(const std::string & p_url)
{
	CString _browser;
	CString _args(p_url.c_str());

	if (m_options.getOption(WEB_BROWSER_PATH, _browser) == true)
	{
		SHELLEXECUTEINFO ExecuteInfo;    
		memset(&ExecuteInfo, 0, sizeof(ExecuteInfo));
    
		ExecuteInfo.cbSize       = sizeof(ExecuteInfo);
		ExecuteInfo.fMask        = 0;                
		ExecuteInfo.hwnd         = 0;                
		ExecuteInfo.lpVerb       = _T("open");
		ExecuteInfo.lpFile       = _browser;
		ExecuteInfo.lpParameters = _args;
		ExecuteInfo.lpDirectory  = 0;
		ExecuteInfo.nShow        = SW_SHOW;
		ExecuteInfo.hInstApp     = 0;    
		if (ShellExecuteEx(&ExecuteInfo) == FALSE)
		{
			AfxMessageBox(_T("Failed to open URL!!"));
			return false;
		}
	}
	else
	{
		AfxMessageBox(_T("Web browser is not set => no way to open URL!"));
		return false;
	}
	return true;
}

bool CpopupApp::getUsers(vector<PopupClientInfo> &users)
{
	PopupClient::UsersList _users;

	CSingleLock _lock(&s_clientmutex);
	_lock.Lock();
	if (m_popupclient != 0)
	{
		_users = m_popupclient->getUsersList();
		PopupClient::UsersList::iterator _it;

		for (_it = _users.begin(); _it != _users.end(); _it++)
		{	
			users.push_back(*_it);
		}
		_lock.Unlock();
		return (users.size() > 0);
	}
	else
	{
		_lock.Unlock();
		return false;
	}
}
